package com.jsjvm.server.dbgproxy.jdwp.data;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import com.jsjvm.shared.jdwp.data.JDWPLocation;


/**
 * OutputStream where a packet payload is first written field by field, then
 * the header is prepended and the packet is written on the desired OutputStream.
 */
public class JDWPOutputStream {
	// buffer to hold the temporary packet
	ByteArrayOutputStream rawPacket = new ByteArrayOutputStream();
	DataOutputStream packet = new DataOutputStream(rawPacket);
	
	// OutputStream to dump the packet into when ready
	DataOutputStream out = null;
	public JDWPOutputStream(OutputStream out) {
		this.out = new DataOutputStream(out);
	}
	
	public void appendInt(int i) throws IOException{
		packet.writeInt(i);
	}
	
	public void appendShort(short s) throws IOException {
		packet.writeShort(s);
	}
	
	public void appendByte(byte b) throws IOException {
		packet.writeByte(b);
	}
	
	public void appendArrayRegion(Object [] array){
		throw new UnsupportedOperationException();
	}
	
	public void appendID(int id) throws IOException{
		packet.writeInt(id);
	}
	
	public void appendTaggedID(int tag, int id) throws IOException{
		packet.write(tag);
		appendID(id);
	}
	
	public void appendLocation(byte tag, JDWPLocation l) throws IOException{
		packet.writeByte(tag);
		appendID(l.getClassID());
		appendID(l.getMethodID());
		packet.writeLong(l.getLocation());
	}
	
	public void appendString(String s) throws IOException{
		packet.writeInt(s.length());
		packet.write(s.getBytes());
	}
	
	public void appendValue(byte tag, Object value) throws IOException{
		switch (tag) {
		case JDWPTag.ARRAY_TAG:
		case JDWPTag.OBJECT_TAG:
		case JDWPTag.STRING_TAG:
		case JDWPTag.THREAD_TAG:
		case JDWPTag.THREAD_G_TAG:
		case JDWPTag.CLASS_LOAD_TAG:
		case JDWPTag.CLASS_TAG:
			appendID((Integer) value); 
			break;
		case JDWPTag.BYTE_TAG:
		case JDWPTag.BOOLEAN_TAG:
			packet.writeByte((Byte)value);
			break;
		case JDWPTag.CHAR_TAG:
			packet.writeChar((Character)value);
			break;
		case JDWPTag.FLOAT_TAG:
			packet.writeFloat((Float)value);
			break;
		case JDWPTag.DOUBLE_TAG:
			packet.writeDouble((Double)value);
			break;
		case JDWPTag.INT_TAG:
			packet.writeInt((Integer)value);
		case JDWPTag.LONG_TAG:
			packet.writeLong((Long)value);
		case JDWPTag.SHORT_TAG:
			packet.writeShort((Short)value);
		}
	}

	public void appendTaggedValue(byte tag, Object value) throws IOException{
		packet.writeByte(tag);
		appendValue(tag, value);
	}

	public void write(byte [] buf) throws IOException{
		out.write(buf);
	}
	
	public void commitReplyPacket(JDWPRequestHeader hdr) throws IOException{
		int length = rawPacket.size() + 11;
		out.writeInt(length);
		out.writeInt(hdr.getId());
		out.writeByte(0x80);
		out.writeShort(0);
		rawPacket.writeTo(out);
		
		rawPacket.reset();
	}

	public void replyWithError(JDWPRequestHeader hdr, short err) throws IOException{
		out.writeInt(11);
		out.writeInt(hdr.getId());
		out.writeByte(0x80);
		out.writeShort(err);
	}

	private static int eventid = 0;
	public void commitEventPacket() throws IOException{
		int length = rawPacket.size() + 11;
		out.writeInt(length);
		out.writeInt(eventid++);
		out.writeByte(0x00);
		out.writeByte(64);		// EventCommandSet
		out.writeByte(100);		// Composite
		rawPacket.writeTo(out);
		
		rawPacket.reset();
	}
}
